<?php
class App_Source_Media_Mapper extends Mozg_Mapper_Data
{
	private $_source;

	protected $_entityPath = 'entity/media';
	protected $_filterPath = 'filter/media';

	public function __construct()
	{
		$this->_source = Mozg_Source::get('default');
	}

	public function GetEntity($data = null)
	{
		$obj = parent::GetEntity($data);
		$obj->SetStatus(0);
		$obj->SetPublishDate(time());
		return $obj;
	}

	private function GetSqlFromFilter($filter = null)
	{
		$sqlFilter = array('where' => array(), 'join' => array());
		if (!($filter instanceof Mozg_Property_Params)) $filter = $this->GetFilter($filter);

		if ($filter->HasSet('id'))
		{
			$sqlFilter['where'][] = 'mm.id = ' .(int)$filter->GetId();
		}

		if (sizeof($filter->GetIds()))
		{
			$ids = array_unique(array_map('intval', $filter->GetIds()));
			$sqlFilter['where'][] = "mm.id in (" .implode(',', $ids). ")";
		}

		if ($filter->HasSet('code'))
		{
			$sqlFilter['where'][] = 'mm.code = ' .$this->_source->quote($filter->GetCode());
		}

		if ($filter->GetNotBlocked())
		{
			$sqlFilter['where'][] = "(mm.status != " .App_Source_Media_Status::NotProcessed. " or !mm.is_blocked)";
		}

		$tagGroups = $filter->GetTagGroups();

		$tags = $filter->GetTags();
		if (sizeof($tags))
		{
			$tagGroups[] = $tags;
		}

		$searchTags = array();
		foreach ($tagGroups as $tags)
		{
			$tagsArr = Mozg_Mapper::Get('Tags')->GetSearchArr($tags);
			if (sizeof($tagsArr))
			{
				$searchStr = '';
				foreach ($tagsArr as $tag)
				{
					$searchStr .= '+",' .trim($this->_source->quote($tag), "'"). '," ';
				}
				$searchTags[] = $searchStr;
			}
		}

		if (sizeof($searchTags))
		{
			/**
			 * поиск по фразе '+",слово1 слово2,"' должен найти строки, в которых есть слово1 и слово2 и в указанном порядке,
			 * но между ними могут быть знаки препинания или другие разделители слов(,+-<> и др.).
			 * А запятые вначале и конце фразы никак не влияют на поиск, поиск ведётся только по целому слову, поэтому
			 * '+",слово1 слово2,"' аналогично +"слово1 слово2"
			 */
			$sqlFilter['where'][] = "MATCH(mm.tags_norm) AGAINST('(" .implode(') (', $searchTags). ")' IN BOOLEAN MODE)";
		}

		if (($filter->GetFromDate() !== null)
			&& ($filter->GetFromDate() !== false))
		{
			$sqlFilter['where'][] = 'mm.date_published >= ' .$this->_source->quote(Mozg::Lib('date')->Format($filter->GetFromDate(), 'Y-m-d H:i'));
		}

		if (($filter->GetToDate() !== null)
			&& ($filter->GetToDate() !== false))
		{
			$sqlFilter['where'][] = 'mm.date_published <= ' .$this->_source->quote(Mozg::Lib('date')->Format($filter->GetToDate(), 'Y-m-d H:i'));
		}

		$sqlFilter['where'] = sizeof($sqlFilter['where']) ? '(' .implode(') and (', $sqlFilter['where']). ')' : '';
		$sqlFilter['join'] = sizeof($sqlFilter['join']) ?  implode(' ', $sqlFilter['join']) : '';

		return $sqlFilter;
	}

	private $_totalRows;

	public function GetTotalRows()
	{
		if (!is_int($this->_totalRows))
		{
			$this->_totalRows = $this->_source->query("SELECT FOUND_ROWS()")->fetchColumn();
		}

		return $this->_totalRows;
	}

	protected function Find($filter = null, Mozg_Paginator $paginator = null, $fields = array())
	{
		$sqlFilter = $this->GetSqlFromFilter($filter);

		$this->_totalRows = null;

        $rows = $this->_source->fetchObjectAll("SELECT " .($paginator ? "SQL_CALC_FOUND_ROWS" : "" ). "
												" .$this->_source->Table('media')->GetSqlMap($fields, "mm"). "
							                 FROM
							                 	%%media%% mm
							                 " .($sqlFilter['where'] ? " WHERE (" .$sqlFilter['where']. ") " : ""). "
							                 ORDER BY mm.date_published DESC, mm.id DESC
							                 " .($paginator ? "LIMIT " .$paginator->GetSqlLimit() : ""). "
							               ", $this->_entityPath);

        return $rows;
	}

	public function GetList($filter = null, Mozg_Paginator $paginator = null)
	{
		return $this->Find($filter, $paginator, array('tags_norm' => 0, 'description' => 0));
	}

	public function GetOne($filter = null)
	{
		return current($this->Find($filter, new Mozg_Paginator(1,1), array('tags_norm' => 0)));
	}

	public function Save(Mozg_Property_Params $obj)
	{
		if (!$obj->Validator()->Validate()) return false;
		if (!$this->Prepare($obj)) return false;

		if ($obj->GetId())
		{
			$filter = $this->GetFilter();
			$filter->SetId($obj->GetId());
			return $this->Update($obj, $filter);
		}

		return $this->Insert($obj);
	}

	protected function Insert(Mozg_Property_Params $obj)
	{
		$addFields = array(
			'tags_norm' => Mozg_Mapper::Get('Tags')->GetSearchStr($obj->GetTags()),
		);

		$fields = $this->_source->Table('media')->GetSqlInsert($obj, '', null, $addFields);
		$num = $this->_source->exec("INSERT INTO %%media%%(" .$fields['fields']. ") VALUES (" .$fields['values']. ")");

		if ($num)
		{
			$obj->SetId($this->_source->lastInsertID());
			Mozg_Mapper::Get('Tags')->Save('media', $obj->GetId(), $obj->GetTags());

			//remove thumb from uploads
			Mozg_Mapper::Get('Uploads')->Delete(array('path' => $obj->GetThumbNail()));

			return true;
		}

		return false;
	}

	protected function Update(Mozg_Property_Params $obj, $filter = null)
	{
		$sqlFilter = $this->GetSqlFromFilter($filter);

		$addFields = array(
			'tags_norm' => Mozg_Mapper::Get('Tags')->GetSearchStr($obj->GetTags()),
		);

		$updateStr = $this->_source->Table('media')->GetSqlUpdate($obj, 'mm', null, $addFields);
		if (!strlen($updateStr)) return true;

		$num = (bool)$this->_source->exec("update %%media%% mm
										      set
									             " .$updateStr. "
											  " .($sqlFilter['where'] ? " WHERE ". $sqlFilter['where'] : ""). "
									     ");

		if ($num)
		{
			Mozg_Mapper::Get('Tags')->Save('media', $obj->GetId(), $obj->GetTags());

			$oldThumbNail = $obj->Orig()->GetThumbNail();
			if ($oldThumbNail != $obj->GetThumbNail())
			{
				//remove thumb from uploads
				Mozg_Mapper::Get('Uploads')->Delete(array('path' => $obj->GetThumbNail()));

				//add old thumb to uploads
				$upload = Mozg_Mapper::Get('Uploads')->GetEntity();
				$upload->SetPath($oldThumbNail);
				Mozg_Mapper::Get('Uploads')->Save($upload);
			}

			return true;
		}

		return false;
	}

	protected function Prepare(Mozg_Property_Params $obj)
	{
		$handler = false;

		if ($obj->HasSet('fileCustom'))
		{
			$filePath = $obj->GetFileCustom();
			$filePath = $filePath['file'];

			if ($filePath != $obj->GetFile())
			{
				$fileInfo = pathinfo($filePath);

				$type = App_Source_Media_Processor::GetMediaTypeByExt($fileInfo['extension']); //video, audio, photo
				if (!$type)
				{
					$obj->Validator()->SetError('fileCustom', Mozg_Lang::_('UNSUPPORTED_FILE_FORMAT'));
					return false;
				}

				try
				{
					$handler = App_Source_Media_Processor::GetHandler($type, _ROOT_PATH.$filePath);
				}
				catch (Exception $e)
				{
					$obj->Validator()->SetError('fileCustom', $e->getMessage());
					return false;
				}

				$handler->SetDestPath($this->GetMediaPath());
				$newFileName = $handler->Convert($fileInfo['filename']);
				if ($handler->IsError())
				{
					$obj->Validator()->SetError('fileCustom', $handler->GetErrorMessage());
					return false;
				}

				$obj->SetCode($fileInfo['filename']);
				$obj->SetStatus($handler->GetConvertStatus());
				$obj->SetType($type);
				$obj->SetMimeType($handler->GetFileMimeType());
				$obj->SetLength($handler->GetFileLength());
				$obj->SetFile($this->GetRelativeFilePath($newFileName));
				$obj->SetFileSize($handler->GetFileSize());

				if ($obj->GetStatus() != App_Source_Media_Status::Ready)
				{
					$upload = Mozg_Mapper::Get('Uploads')->GetEntity();
					$upload->SetPath($obj->GetFile());
					Mozg_Mapper::Get('Uploads')->Save($upload);
				}
			}
        }

		if ($obj->HasSet('thumbCustom'))
		{
			$thumb = $obj->GetThumbCustom();
			$obj->SetThumbNail(isset($thumb['file']) ? $thumb['file'] : '');
		}

		if (!$obj->GetThumbNail() && $handler)
		{
			$thumb = $this->GetRelativeFilePath($handler->CreateThumbNail());
			$obj->SetThumbNail($thumb);

			$upload = Mozg_Mapper::Get('Uploads')->GetEntity();
			$upload->SetPath($thumb);
			Mozg_Mapper::Get('Uploads')->Save($upload);
		}

        return true;
	}

    private function GetMediaPath()
    {
    	return _ROOT_PATH. 'static/files/_media/' .date('Y/m/d/H/');
    }

	private function GetRelativeFilePath($fullFilePath)
	{
		$fullFilePath = str_replace('\\', '/', $fullFilePath);
		$mediaPath = str_replace('\\', '/', _ROOT_PATH);

		if (0 === strpos($fullFilePath, $mediaPath))
		{
			$fullFilePath = substr($fullFilePath, strlen($mediaPath));
		}

		return $fullFilePath;
	}

    public function ApproveMediaByCode($code)
    {
    	$obj = $this->GetOne(array('code' => $code));
    	if (!$obj) return false;

    	$filePath = _ROOT_PATH.$obj->GetFile();

    	$fileCheckResult = false;

   		try
		{
			$handler = App_Source_Media_Processor::GetHandler($obj->GetType(), $filePath);

			$obj->SetStatus(App_Source_Media_Status::Ready);
			$obj->SetLength($handler->GetFileLength());
			$obj->SetFileSize($handler->GetFileSize());

			$fileCheckResult = true;
		}
		catch (Exception $e)
		{
			$this->SetErrorMessage($e->getMessage());

			$obj->SetStatus(App_Source_Media_Status::HasError);
		}

		$obj->SetIsBlocked(0);

		$updateResult = $this->Update($obj, array('id' => $obj->GetId()));

		$finalResult = $updateResult && $fileCheckResult;
		if ($finalResult)
		{
			Mozg_Mapper::Get('Uploads')->Delete(array('path' => $obj->GetFile()));
		}

		return $finalResult;
    }

    public function Delete($filter = null)
    {
    	$sqlFilter = $this->GetSqlFromFilter($filter);

    	$objs = $this->GetList($filter);

        $num = (bool)$this->_source->exec("DELETE
        											mm, mme, mt
		        								FROM
		        									%%media%% mm
		        										left join %%media_ext%% mme on(mm.id = mme.media_id)
		        										left join %%tag%% mt on (mm.id = mt.id AND area = 'media')
		        								" .($sqlFilter['where'] ? " WHERE ". $sqlFilter['where'] : ""). "
		        								");
    	if ($num)
    	{
    		foreach ($objs as $obj)
    		{
				$upload = Mozg_Mapper::Get('Uploads')->GetEntity();
				$upload->SetPath($obj->GetFile());
				Mozg_Mapper::Get('Uploads')->Save($upload);

				$upload = Mozg_Mapper::Get('Uploads')->GetEntity();
				$upload->SetPath($obj->GetThumbNail());
				Mozg_Mapper::Get('Uploads')->Save($upload);
    		}

    		return true;
    	}

    	return false;
    }
}